[Py 百日馬 Day 4-1] random module 常見方法 - 產生隨機數與亂數取樣/排序


Posted by Laihan on 2022-02-03

在 Python 中想要隨機產生某數或抽籤功能可以使用 random 模組,這模組可以用來產生偽隨機數 (pseudo-random numbers),產生方式其實是用某種公式算出來的,至於為什麼不使用真隨機產生,可以參考這篇

random module 不適合使用在安全性或加密用途
如果有此需求可以使用 secrets module

下面會針對隨機產生數字、隨機抽取出元素列出一些常用的方法

指定範圍內隨機產生一個整數

1. randrange(start, stop, step)

如果 start > stop 會發生 ValueError;回傳值範圍為 start <= 回傳值 < stop

參數 敘述 預設值
start 指定隨機數範圍起點,選填。 0
stop 指定隨機數範圍終點,必填。 n/a
step 指定隨機抽取的間隔,選填。 1
import random    # 務必要先引入
print(random.randrange(0, 5, 2))   # 可能出現 0, 2, 4 其ㄧ
print(random.randrange(1, 5))      # 可能出現 1, 2, 3, 4 其ㄧ
print(random.randrange(5))         # 可能出現 0, 1, 2, 3, 4 其ㄧ

2. randint(a, b)

如果 a > b 會發生 ValueError,回傳值範圍為 a <= 回傳值 <= b ,有沒有覺得跟 randrange 很雷同,其實 randint 就是 randrange(a, b + 1) 的另一種寫法。

import random    # 務必要先引入
print(random.randint(1, 5)     # 可能出現 1, 2, 3, 4, 5 其ㄧ
print(random.randrange(1, 6)   # 可能出現 1, 2, 3, 4, 5 其ㄧ

隨機產生一個浮點數

1. random()

會產生一個 0.0 ~ 1.0 之間的任一浮點數。

import random    # 務必要先引入
print(random.random())   # 0.9016921451498453

2. uniform(a, b)

回傳值範圍為 a <= 回傳值 <= b

import random    # 務必要先引入
print(random.uniform(10, 50))   # 49.98014509364687

從序列隨機抽取

序列是元素的集合,如: stringListTuple 等,可針對序列做隨機相關的方法,這讓我想到以前數學課 C 幾取幾的東東。

1. choice(seq) 多選一

從序列中隨機取得一個元素,seq 為空會產生 IndexError

import random    # 務必要先引入
print(random.choice(["一", "二", "三"]))   # "二"

2. choices(population, k=n) 取後放回

從序列中隨機抽取 n 個元素,可重複抽取,會回傳 List,population 為空會產生 IndexError;另有參數可以配置權重,提高選中機率,細節可參見最後的參考資料。

import random    # 務必要先引入
print(random.choices(["一", "二", "三", "四"], k=3))   # ["一", "三", "一"]

3. sample(population, k=n) 取後不放回

從序列中隨機抽取 n 個元素,不重複抽取,會回傳 List,如果 n > len(population) (樣本數 > 母體數) 會產生 ValueError;有其他參數可提高選中機率,細節可參見最後的參考資料。

import random    # 務必要先引入
print(random.sample(["一", "二", "三", "四"], k=3))   # ["一", "三", "四"]

4. shuffle(x) 隨機排序

將傳入的 list 原地 (in place) 打亂排序;若想保留原本的陣列,回傳一個新的打亂後 list,可以使用 sample(x, k=len(x))

import random    # 務必要先引入

my_list = ["一", "二", "三", "四"]

# shuffle 將傳入陣列原地隨機排序
random.shuffle(my_list)
print(my_list)   # ["一", "三", "四", "二"]

# shuffle 不會回傳值
new_list = random.shuffle(my_list)
print(new_list)   # None

# sample 可以回傳一個打亂後的新 list
ori_list = ["一", "二", "三", "四"]
new_list = random.sample(ori_list, k=len(ori_list))
print(ori_list)    #  ["一", "二", "三", "四"]
print(new_list)    #  ["三", "四", "二", "一"]

隨機結果再現

這部分我覺得有點難以想像,所以就舉 RPG 的例子來幫助理解。

1. seed(a) 指定章節開始

可以初始化亂數產生器,a 預設值為 None ,a = None 時會自動帶入當前的系統時間,可以指定 a 來獲得一樣的亂數值。

import random    # 務必要先引入
# case A
random.seed(10)
print(random.random())   # 0.5714025946899135
print(random.random())   # 0.4288890546751146

# case B
random.seed(100)
print(random.random())    # 0.1456692551041303

# case C: 將 seed 設回 10,會得到跟 case A 一樣的結果
random.seed(10)
print(random.random())   # 0.5714025946899135
print(random.random())   # 0.4288890546751146

2. getstate()、setstate(state_obj) 接續進度

getstate() 存檔功能

會回傳一個產生器當前狀態的物件;如要恢復當初的狀態,要將物件傳遞給 setstate()

setstate(state_obj) 讀檔功能

傳入從 getstate() 得到的物件,可以恢復產生器在調用 getstate() 時的狀態。

import random    # 務必要先引入

# 不初始化也可以透過 getstate、setstate 來獲得一致結果
# 只是產生的隨機數基本上會跟下面例子不一樣
random.seed(10)

# 將目前產生器狀態存檔
state = random.getstate()
print(random.random())   # 0.5714025946899135
print(random.random())   # 0.4288890546751146

# 讀檔以重現存檔時的狀態
random.setstate(state)
print(random.random())   # 0.5714025946899135
print(random.random())   # 0.4288890546751146

參考資料


#Python







Related Posts

How to solve the perpetual loading issue in Evernote? Evernote 一直轉圈圈的解決辦法

How to solve the perpetual loading issue in Evernote? Evernote 一直轉圈圈的解決辦法

1280. Students and Examinations

1280. Students and Examinations

類別繼承範例

類別繼承範例


Comments